package stripsPlanner;

import java.util.ArrayList;
import java.util.List;

import exceptions.NoResolveException;

import stripsLanguage.Link;
import stripsLanguage.Ordering;
import stripsLanguage.Plan;
import stripsLanguage.Predicate;
import stripsLanguage.Step;

public class ResolveThreats {

	public ResolveThreats() {}
	
	// klasa pomocnicza do zwracania danych z findThreats
	protected class InnerClass {
		private List<Step> ThreatSteps = null;
		private List<Link> ThreatedLinks = null;
		public InnerClass(
			List<Step> threatSteps,
			List<Link> threatedLinks)	
		{
			ThreatSteps = threatSteps;
			ThreatedLinks = threatedLinks;			
		}
		
		public List<Step> getThreatSteps() {return ThreatSteps;}
		public List<Link> getThreatedLinks() {return ThreatedLinks;}
	}
	
	/** zwraca znalezione zagrozenia
	 * 
	 * @param links
	 * @param steps
	 * @param cube
	 * TEST
	 */
	protected InnerClass findThreats(
		List<Step> steps,
		List<Link> links,
		List<List<Integer>> matrix)
	{
		List<Step> threatSteps = new ArrayList<Step>();	
		List<Link> threatedLinks = new ArrayList<Link>();
		for(Link link: links)
		{
			/*** przeszukiwanie polaczen dochodzacych do danego wierzcholka***/			
			// na listach <L> zbadac wszystkie elementy przed danym
			// i sprawdzic, czy nie ma zaprzeczenia danego predykatu
			int idOfLastStep = link.getLastStep().getId();
			int idOfFirstStep = link.getFirstStep().getId();
			//przeszukiwac linearyzacje, czy nie ma zaprzeczenia tego Linka
			//czyli, dla kazdego wierzcholka z tych sprawdzane jest, czy nie zaprzecza badanemu predykatowi
			// UWAGA. Szuka tylko w wierzcholkach z przedzialu <pierwszy zLinka,drugi z Linka>
			for(List<Integer> list: matrix)
			{
				boolean interval = false;
				for(int i = 0; i < list.size(); ++i)
				{
					boolean threat = false;
					int id = list.get(i);
					// badaj, jesli zagrozenie jest przed tym elementem
					if(id == idOfFirstStep) //
					{
						interval = true;
						continue;
					}
					else
					{
						if(!interval)
							continue;
						if(id == idOfLastStep) // dalej nie szukamy
							break;
					}
					Step step = steps.get(id-1);
					for(Predicate p: step.getEffects()) //sprawdza predykati
					{
						if(p.getNrOfType() != link.getFullilledPredicate().getNrOfType() // jeslli rozny typ
							|| p.getNegation()) // lub predykat nie jest negacja
							continue;
						threat = true;
						for(int j = 0; j < p.getArguments().size(); ++j)
						{
							int idOfIns1 = p.getInstance(j);
							int idOfIns2 = link.getFullilledPredicate().getInstance(j);
							if(idOfIns1 < 0
								|| idOfIns2 < 0
								|| idOfIns1 != idOfIns2)							
							{
								threat = false;
								break;
							}
						}
						if(threat)
						{							
							for(int k = 0; k < threatSteps.size(); ++k) // jak juz jest taki, to nie dodawaj
							{
								if(threatedLinks.get(k).getFirstStep().getId() == link.getFirstStep().getId()
									&& threatedLinks.get(k).getLastStep().getId() ==link.getLastStep().getId()
									&& threatedLinks.get(k).getFullilledPredicate().getIdOfPredicate()
										== link.getFullilledPredicate().getIdOfPredicate()
									&& step.getId() == threatSteps.get(k).getId())
									threat = false;
							}
							if(threat)
							{	
								threatedLinks.add(link);
								threatSteps.add(step);						
							}
							break;
						}
					}
					//if(threat)
					//	break;
				}
			}
		}
		return new InnerClass(
			threatSteps,			
			threatedLinks);
	}
	
	public void resolveThreats(Plan plan) throws NoResolveException
	{
		// stworz wszystkie mozliwe linearyzacje
		List<List<Integer>> matrix
			= new ChooseOperator().buildLinearization(
				plan.getSteps(),
				plan.getOrderings());
		
		// znajdz zagrozenia
		InnerClass ic = findThreats(plan.getSteps(),plan.getLinks(),matrix);
		
		if(ic.getThreatedLinks().size() > 0) // jesli conajmniej jedno zagrozenie
			solveThreats(
				ic,		
				plan.getSteps(),
				plan.getOrderings());
	}
	
	/**
	 * mozliwe rozwiazania:
	 * - degradacja
	 * - promocja
	 * - nawrot
	 * @param ic
	 * @throws NoResolveException
	 * TEST 
	 */
	protected void solveThreats(
		InnerClass ic,
		List<Step> steps,
		List<Ordering> orderings) throws NoResolveException
	{
		List<Step> threatSteps = ic.getThreatSteps();
		List<Link> threatedLinks = ic.getThreatedLinks();

		for(int i = 0; i < threatedLinks.size(); ++i)
		{
			List<List<Integer>> matrix
				= new ChooseOperator().buildLinearization(
					steps,
					orderings);
			
//			 System.out.println("Link: " + threatedLinks.get(i).getFirstStep().getId() + " -> "
//			 + threatedLinks.get(i).getLastStep().getId() + ", zagrozony przez: " 
//			 + threatSteps.get(i).getId());
			if(new ChooseOperator().isOnceAfter(
					matrix,
					threatSteps.get(i).getId(),
					threatedLinks.get(i).getLastStep().getId()))	// promocja
				{
//					System.out.println("Promocja: " + threatedLinks.get(i).getLastStep().getId() + " -> "
//						+ threatSteps.get(i).getId());
					orderings.add(new Ordering(
						threatedLinks.get(i).getLastStep(),
						threatSteps.get(i)));
				}
			else if(new ChooseOperator().isOnceAfter(
				matrix,
				threatedLinks.get(i).getFirstStep().getId(),
				threatSteps.get(i).getId()))	//degradacja
			{
//				System.out.println("Degradacja: " + threatSteps.get(i).getId() + " ->"
//					+  threatedLinks.get(i).getFirstStep().getId());				
				orderings.add(new Ordering(
					threatSteps.get(i),
					threatedLinks.get(i).getFirstStep()));
			} else
				throw new NoResolveException("Brak rozwiazania - trzeba cofnac");
		}
	}
	
}
